﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using Hohlios.TMDBWrapper.DotNet.Model;

namespace Hohlios.TMDBWrapper.DotNet
{
    public class TMDBWrapper
    {
        private const string MOVIE_GET_INFO_URL = "http://api.themoviedb.org/2.1/Movie.getInfo/en/json/{0}/{1}";
        private const string MOVIE_IMDB_LOOKUP_URL = "http://api.themoviedb.org/2.1/Movie.imdbLookup/en/json/{0}/{1}";
        private const string MOVIE_SEARCH_URL = "http://api.themoviedb.org/2.1/Movie.search/en/json/{0}/{1}{2}";
        private const string MOVIE_GET_IMAGES_URL = "http://api.themoviedb.org/2.1/Movie.getImages/en/json/{0}/{1}";
        private const string MOVIE_BROWSE_URL = "http://api.themoviedb.org/2.1/Movie.browse/en-US/json/{0}?{1}";

        private const string PERSON_SEARCH_URL = "http://api.themoviedb.org/2.1/Person.search/en/json/{0}/{1}";
        private const string PERSON_GET_INFO_URL = "http://api.themoviedb.org/2.1/Person.getInfo/en/json/{0}/{1}";
        
        public string APIKey { get; set; }

        public TMDBWrapper(string apiKey)
        {
            APIKey = apiKey;
        }

        /// <summary>
        /// The Movie.search method is the easiest and quickest way to search for a movie. 
        /// It is a mandatory method in order to get the movie id to pass to (as an example) the Movie.getInfo method.
        /// </summary>
        /// <param name="completeAction">Action to be completed after async call</param>
        /// <param name="title">Movie Title for search</param>
        /// <param name="year">(Optional) Year to further define title search</param>
        public void MovieSearch(Action<List<Movie>> completeAction, string title, string year = null)
        {
            year = string.IsNullOrEmpty(year) ? "" : string.Format("+{0}", year);
            DoWebCall(string.Format(MOVIE_SEARCH_URL, APIKey, string.Join("+", title.Split(' ')), year), completeAction);
        }

        /// <summary>
        /// The Movie.imdbLookup method is the easiest and quickest way to search for a movie based on it's IMDb ID.
        /// You can use Movie.imdbLookup method to get the TMDb id of a movie if you already have the IMDB id.
        /// </summary>
        /// <param name="completeAction">Action to be completed after async call</param>
        /// <param name="id">IMDB id for search</param>
        public void MovieIMDBLookup(Action<List<Movie>> completeAction, string id)
        {
            DoWebCall(string.Format(MOVIE_IMDB_LOOKUP_URL, APIKey, id), completeAction);
        }

        /// <summary>
        /// The Movie.getInfo method is used to retrieve specific information about a movie. 
        /// Things like overview, release date, cast data, genre's, YouTube trailer link, etc...
        /// </summary>
        /// <param name="completeAction">Action to be completed after async call</param>
        /// <param name="id">id for search. This id is specific to TMDB and can be found by SearchMovieTitle, SearchMovieByIMDBId, BrowseMovie</param>
        public void MovieGetInfo(Action<List<MovieInfo>> completeAction, string id)
        {
            DoWebCall(string.Format(MOVIE_GET_INFO_URL, APIKey, id), completeAction);
        }

        /// <summary>
        /// The Movie.browse method is probably the most powerful single method on the entire TMDb API. 
        /// While it might not be used by all apps, it is a great place to start if you're interested in building any kind of a top 10 list.
        /// </summary>
        /// <param name="completeAction">Action to be completed after async call</param>        
        /// <param name="options">Model for holding all filter combinations</param>
        public void MovieBrowse(Action<List<Movie>> completeAction, MovieBrowseOptions options)
        {
            DoWebCall(string.Format(MOVIE_BROWSE_URL, APIKey, options.GetFilters()), completeAction);
        }

        public void SearchPerson(string name, Action<List<Person>> completeAction)
        {
            DoWebCall(string.Format(PERSON_SEARCH_URL, APIKey, name), completeAction);
        }

        public void GetPersonInfo(string id, Action<List<PersonInfo>> completeAction)
        {
            DoWebCall(string.Format(PERSON_GET_INFO_URL, APIKey, id), completeAction);
        }

        public void DoWebCall<T>(string uri, Action<T> completeAction)
        {
            var webClient = new WebClient();
            webClient.OpenReadCompleted += (s, e) => ParseJSON(e.Result, completeAction);
            webClient.OpenReadAsync(new Uri(uri));
        }

        private static void ParseJSON<T>(Stream stream, Action<T> completeAction)
        {
            var jsonSerializer = new DataContractJsonSerializer(typeof(T));

            var result = default(T);
            
            try
            {
                result = (T) jsonSerializer.ReadObject(stream);
            }
            catch
            {
                //This is here because a bad search returns serializer breaking messages instead of empty.
            }

            completeAction(result);
        }
    }
}
